1 Wstęp

Celem niniejszego raportu jest przeprowadzenie analizy danych dotyczących rynku nieruchomości. Wykorzystano w tym celu szereg technik eksploracyjnych oraz metod przetwarzania danych, takich jak analiza brakujących wartości, imputacja danych, identyfikacja obserwacji odstających oraz wizualizacja wyników.

Raport jest podzielony na kilka kluczowych sekcji, każda z nich koncentruje się na innym aspekcie analizy. Na początku zidentyfikowane zostaną brakujące dane, a następnie zostaną przeprowadzone transformacje mające na celu poprawę jakości danych. Kolejne sekcje poświęcone są analizie statystycznej oraz wizualizacji danych, co pozwoli lepiej zrozumieć zależności panujące na rynku nieruchomości.

agencja_nieruchomosci <- read.csv("agencja_nieruchomosci.csv")
knitr::kable(head(agencja_nieruchomosci, 10)) %>%
  kable_styling(font_size = 10)
price area bedrooms bathrooms stories mainroad guestroom basement hotwaterheating airconditioning parking prefarea furnishingstatus
NA 7420 4 2 3 NA no no no yes 2 yes furnished
12250000 8960 4 4 4 yes no no no yes 3 no furnished
12250000 9960 3 2 2 yes no yes no no 2 yes semi-furnished
12215000 7500 4 2 2 yes no yes no yes 3 yes furnished
11410000 7420 4 1 2 yes yes yes no yes 2 no furnished
10850000 7500 3 3 1 yes no yes no yes 2 yes semi-furnished
10150000 8580 4 3 4 yes no no no yes 2 yes semi-furnished
10150000 16200 5 3 2 yes no no no no 0 no unfurnished
9870000 8100 4 1 2 yes yes yes no yes 2 yes furnished
9800000 5750 3 2 4 yes yes no no yes 1 yes unfurnished

2 Dane brakujące

Pierwszym etapem analizy jest identyfikacja brakujących wartości w zbiorze danych. Braki w danych mogą wynikać z wielu czynników, takich jak błędy w zbieraniu informacji czy celowe pominięcie pewnych elementów. W tej sekcji dokonano identyfikacji oraz przedstawiono sposoby radzenia sobie z brakującymi danymi.

Analiza wykazała, że najwięcej brakujących wartości występuje w kolumnie cena. Braki te mogą wynikać z strategii sprzedających, którzy decydują się nie podawać cen, aby zebrać oferty od potencjalnych kupców.

2.1 Początkowa liczba i proporcje NA

Ogólne podsumowanie brakujących wartości

knitr::kable(miss_var_summary(agencja_nieruchomosci))
variable n_miss pct_miss
price 110 20.2
mainroad 50 9.17
prefarea 50 9.17
area 0 0
bedrooms 0 0
bathrooms 0 0
stories 0 0
guestroom 0 0
basement 0 0
hotwaterheating 0 0
airconditioning 0 0
parking 0 0
furnishingstatus 0 0

Wprowadzając podstawowe reguły, na przykład weryfikując czy cena nie jest ujemna, mozemy zweryfikować sensowność naszych danych. Po określeniu reguł są one upraszczane. Funkcja simplify_rules pozwala sprawdzić czy nie ma w nich sprzeczności i wyeliminować duplikaty. W tym przypadku wyczyszczenie reguł nie przyniosło zadnych efektów.

rules <- validator(
  price > 0 & price < 100000000,
  area > 0 & area < 100000,
  bedrooms > -1 & bedrooms < 10,
  bathrooms > -1 & bathrooms < 10,
  stories > -1 & stories < 20,
  parking > -1 & parking < 5,
  mainroad %in% c("yes", "no"),
  guestroom %in% c("yes", "no"),
  basement %in% c("yes", "no"),
  hotwaterheating %in% c("yes", "no"),
  airconditioning %in% c("yes", "no"),
  prefarea %in% c("yes", "no"),
  furnishingstatus %in% c(
    "furnished",
    "semi-furnished",
    "unfurnished"
  )
)

warnings()

validation_results <- confront(agencja_nieruchomosci, rules)
knitr::kable(summary(validation_results))
name items passes fails nNA error warning expression
V01 545 435 0 110 FALSE FALSE price > 0 & price < 1e+08
V02 545 545 0 0 FALSE FALSE area > 0 & area < 1e+05
V03 545 545 0 0 FALSE FALSE bedrooms > -1 & bedrooms < 10
V04 545 545 0 0 FALSE FALSE bathrooms > -1 & bathrooms < 10
V05 545 545 0 0 FALSE FALSE stories > -1 & stories < 20
V06 545 545 0 0 FALSE FALSE parking > -1 & parking < 5
V07 545 495 0 50 FALSE FALSE mainroad %vin% c(“yes”, “no”)
V08 545 545 0 0 FALSE FALSE guestroom %vin% c(“yes”, “no”)
V09 545 545 0 0 FALSE FALSE basement %vin% c(“yes”, “no”)
V10 545 545 0 0 FALSE FALSE hotwaterheating %vin% c(“yes”, “no”)
V11 545 545 0 0 FALSE FALSE airconditioning %vin% c(“yes”, “no”)
V12 545 495 0 50 FALSE FALSE prefarea %vin% c(“yes”, “no”)
V13 545 545 0 0 FALSE FALSE furnishingstatus %vin% c(“furnished”, “semi-furnished”, “unfurnished”)
barplot(validation_results, main = "price")

Funkcje summary i barplot zgodnie pokazują ze wszystkie nasze dane mają realne wartości.

Aby ułatwić dalszą pracę można zmienić wartości binarne yes i no na 0 i 1. Wartościom z kolumny furnished nadajemy kolejno wartości 0, 1 i 2 (od unfurnished do furnished).

agencja_nieruchomosci$mainroad <-
  ifelse(agencja_nieruchomosci$mainroad == "yes", 1, 0)
agencja_nieruchomosci$guestroom <-
  ifelse(agencja_nieruchomosci$guestroom == "yes", 1, 0)
agencja_nieruchomosci$basement <-
  ifelse(agencja_nieruchomosci$basement == "yes", 1, 0)
agencja_nieruchomosci$hotwaterheating <-
  ifelse(agencja_nieruchomosci$hotwaterheating == "yes", 1, 0)
agencja_nieruchomosci$airconditioning <-
  ifelse(agencja_nieruchomosci$airconditioning == "yes", 1, 0)
agencja_nieruchomosci$prefarea <-
  ifelse(agencja_nieruchomosci$prefarea == "yes", 1, 0)

agencja_nieruchomosci$furnishingstatus <- ifelse(
  agencja_nieruchomosci$furnishingstatus == "unfurnished",
  0,
  ifelse(
    agencja_nieruchomosci$furnishingstatus == "semi-furnished",
    1,
    2
  )
)
knitr::kable(head(agencja_nieruchomosci, 10)) %>%
  kable_styling(font_size = 10)
price area bedrooms bathrooms stories mainroad guestroom basement hotwaterheating airconditioning parking prefarea furnishingstatus
NA 7420 4 2 3 NA 0 0 0 1 2 1 2
12250000 8960 4 4 4 1 0 0 0 1 3 0 2
12250000 9960 3 2 2 1 0 1 0 0 2 1 1
12215000 7500 4 2 2 1 0 1 0 1 3 1 2
11410000 7420 4 1 2 1 1 1 0 1 2 0 2
10850000 7500 3 3 1 1 0 1 0 1 2 1 1
10150000 8580 4 3 4 1 0 0 0 1 2 1 1
10150000 16200 5 3 2 1 0 0 0 0 0 0 0
9870000 8100 4 1 2 1 1 1 0 1 2 1 2
9800000 5750 3 2 4 1 1 0 0 1 1 1 0

Brakujące wartości wg. stanu umeblowania

agencja_nieruchomosci %>%
  group_by(furnishingstatus) %>%
  miss_var_summary() %>%
  filter(n_miss > 0) %>%
  knitr::kable()
furnishingstatus variable n_miss pct_miss
2 price 33 23.6
2 prefarea 15 10.7
2 mainroad 11 7.86
1 price 43 18.9
1 mainroad 23 10.1
1 prefarea 18 7.93
0 price 34 19.1
0 prefarea 17 9.55
0 mainroad 16 8.99
agencja_nieruchomosci %>%
  miss_case_table() %>%
  knitr::kable()
n_miss_in_case n_cases pct_cases
0 363 66.6055046
1 155 28.4403670
2 26 4.7706422
3 1 0.1834862

W tabeli podsumowującej widzimy, że liczba pustych pól na obserwację waha się o. Spośród 545 obserwacji 363 jest kompletnych - to 66,6% obserwacji w danych. Tylko 1 obserwacja (0,18%) zawiera 3 brakujące wartości.

2.2 Wizualizacja NA

vis_miss(agencja_nieruchomosci)

gg_miss_fct(agencja_nieruchomosci, fct=furnishingstatus)

gg_miss_fct(agencja_nieruchomosci, fct=stories)

gg_miss_fct(agencja_nieruchomosci, fct=parking)

Dzięki wykresom, które przedstawiają procent brakujących danych w zależności od filtru można wywnioskować, że największy procent brakujących danych jest zazwyczaj w momencie, gdzie jest najwieksza wartość dodatnia z konkretnych filtrów. Przykładowo największa ilość NA (price) jest w momencie, gdzie są 3 miejsca parkingowe.

2.3 Przekroje NA

gg_miss_upset(agencja_nieruchomosci, nsets=3)

Na powyższym wykresie widać, że najwięcej brakujących danych jest w kolumnie cena. Przyczyny są prawdopodobnie czysto praktycznie - za wysoka cena może odstraszyć potencjalnych klientów. Potencjalną przyczyną może oznaczać chęć zbierania ofert.

2.4 Zależności pomiędzy brakującymi danymi.

ggplot(data = agencja_nieruchomosci, aes(x = area, y = price)) +
  geom_point() +
  geom_miss_point() +
  scale_color_manual(values = c("darkorange", "cyan4")) +
  theme_minimal()

W pierwszym przypadku widzimy, że brak wartości price był podobnie prawdopodobny do większości przypadków powierzchni domu. Można zauważyć brak NA w wielkości domu od 12 tys. do 16 tys. jednak są tam tylko pojedyncze wartości.

ggplot(data = agencja_nieruchomosci, aes(x = area, y = price)) +
  geom_point() +
  geom_miss_point() +
  scale_color_manual(values = c("darkorange", "cyan4")) +
  theme_minimal() +
  facet_wrap(~bedrooms)

W podziale na podwykresy widzimy, że najwięcej NA znajduje sie w nieruchomościach, które mają 3 sypialnie.

3 Czyszczenie i przeważanie danych

W tej części raportu dokonano przekształcenia danych w taki sposób, aby ułatwić dalszą analizę. Zastosowano metody imputacji danych, takie jak k-NN oraz imputacja liniowa, aby uzupełnić brakujące wartości. Ponadto dokonano konwersji zmiennych kategorycznych do postaci numerycznej, co umożliwia ich dalsze wykorzystanie w modelach analitycznych.

Zidentyfikowano również obserwacje odstające w zmiennych liczbowych, które mogą wpływać na jakość analizy. Wykorzystano miary rozkładu oraz wykresy pudełkowe do wizualizacji i usunięcia skrajnych wartości, które mogłyby zaburzać wyniki.

3.1 Imputacje danych

Identyfikacja brakujących danych jest kluczowym elementem czyszczenia datasetu. Kolejnym krokiem jest imputacja, czyli uzupełnienie brakujących komórek. Jest wiele metod umożliwiających imputację. Jedną z najpopularniejszych metod jest algorytm k najbliższych sąsiadów (kNN). Poniżej wykorzystano tę metodę to uzupełnienia brakujących cen. Niezbędnym jest tutaj określenie ilości sąsiadów (k) branych pod uwagę przy regresji, arbitralnie wybrana została wartość k=15.

agencja_nieruchomosci <- kNN(
  agencja_nieruchomosci,
  variable = "price",
  k = 15
)
agencja_nieruchomosci <- subset(agencja_nieruchomosci, select = -price_imp)
knitr::kable(miss_var_summary(agencja_nieruchomosci))
variable n_miss pct_miss
mainroad 50 9.17
prefarea 50 9.17
price 0 0
area 0 0
bedrooms 0 0
bathrooms 0 0
stories 0 0
guestroom 0 0
basement 0 0
hotwaterheating 0 0
airconditioning 0 0
parking 0 0
furnishingstatus 0 0

Kolejną znaną metodą jest imputacja liniowa. Zmienne brakujące w kolumnie mainroad uzupełnione zostały wykorzystując metodę impute_lm pochodzącą z pakietu simputation. Wartości te imputujemy na podstawie zmiennych price oraz parking.

agencja_nieruchomosci <- impute_lm(
  agencja_nieruchomosci,
  variable = "mainroad",
  formula = mainroad ~ price + parking,
)
agencja_nieruchomosci$mainroad <- round(agencja_nieruchomosci$mainroad)

knitr::kable(miss_var_summary(agencja_nieruchomosci))
variable n_miss pct_miss
prefarea 50 9.17
price 0 0
area 0 0
bedrooms 0 0
bathrooms 0 0
stories 0 0
mainroad 0 0
guestroom 0 0
basement 0 0
hotwaterheating 0 0
airconditioning 0 0
parking 0 0
furnishingstatus 0 0

Dane w ostatniej brakującej kolumnie uzupełniamy metodą mice czyli Multivariate Imputation by Chained Equations - wielowymiarowe wypełnianie przez równania łańcuchowe. Jako że uzupełniamy zmienną binarną, korzystamy z metody logreg.

imputed_data <- mice(agencja_nieruchomosci, method = "logreg", m = 5, printFlag = FALSE)
completed_data <- complete(imputed_data, 1)
agencja_nieruchomosci <- completed_data

Zobaczmy jak wygląda pierwsze 10 wierszy naszego zbioru danych po imputacji:

knitr::kable(head(agencja_nieruchomosci, 10)) %>%
  kable_styling(font_size = 10)
price area bedrooms bathrooms stories mainroad guestroom basement hotwaterheating airconditioning parking prefarea furnishingstatus
7910000 7420 4 2 3 1 0 0 0 1 2 1 2
12250000 8960 4 4 4 1 0 0 0 1 3 0 2
12250000 9960 3 2 2 1 0 1 0 0 2 1 1
12215000 7500 4 2 2 1 0 1 0 1 3 1 2
11410000 7420 4 1 2 1 1 1 0 1 2 0 2
10850000 7500 3 3 1 1 0 1 0 1 2 1 1
10150000 8580 4 3 4 1 0 0 0 1 2 1 1
10150000 16200 5 3 2 1 0 0 0 0 0 0 0
9870000 8100 4 1 2 1 1 1 0 1 2 1 2
9800000 5750 3 2 4 1 1 0 0 1 1 1 0

3.2 Transformacje

completed_data$price <- as.numeric(completed_data$price)
completed_data <- completed_data %>%
  mutate(
    price_z = (price - mean(price, na.rm = TRUE)) / sd(price, na.rm = TRUE),
    price_minmax = scales::rescale(price, to = c(0, 1))
  )
boxplot(completed_data$price_z, completed_data$price_minmax,
        names = c("Z-score", "Min-max"), main = "Porównanie normalizacji danych")

skewness_results <- sapply(completed_data, function(x) if (is.numeric(x)) e1071::skewness(x, na.rm = TRUE) else NA)
knitr::kable(skewness_results)
x
price 1.2284480
area 1.3139246
bedrooms 0.4929587
bathrooms 1.5805260
stories 1.0761391
mainroad -2.2903089
guestroom 1.6791359
basement 0.6251337
hotwaterheating 4.3294938
airconditioning 0.7913726
parking 0.8374328
prefarea 1.2236374
furnishingstatus 0.1170196
price_z 1.2284480
price_minmax 1.2284480

3.3 Obserwacje odstające

outlier_report <- diagnose_outlier(completed_data)
knitr::kable(outlier_report)
variables outliers_cnt outliers_ratio outliers_mean with_mean without_mean
price 13 2.3853211 1.053123e+07 4.721686e+06 4.579723e+06
area 12 2.2018349 1.269325e+04 5.150541e+03 4.980724e+03
bedrooms 12 2.2018349 5.166667e+00 2.965138e+00 2.915572e+00
bathrooms 1 0.1834862 4.000000e+00 1.286239e+00 1.281250e+00
stories 41 7.5229358 4.000000e+00 1.805505e+00 1.626984e+00
mainroad 67 12.2935780 0.000000e+00 8.770642e-01 1.000000e+00
guestroom 97 17.7981651 1.000000e+00 1.779817e-01 0.000000e+00
basement 0 0.0000000 NaN 3.504587e-01 3.504587e-01
hotwaterheating 25 4.5871560 1.000000e+00 4.587160e-02 0.000000e+00
airconditioning 0 0.0000000 NaN 3.155963e-01 3.155963e-01
parking 12 2.2018349 3.000000e+00 6.935780e-01 6.416510e-01
prefarea 130 23.8532110 1.000000e+00 2.385321e-01 0.000000e+00
furnishingstatus 0 0.0000000 NaN 9.302752e-01 9.302752e-01
price_z 13 2.3853211 3.269696e+00 0.000000e+00 -7.989860e-02
price_minmax 13 2.3853211 8.363077e-01 2.830177e-01 2.694975e-01
dane <- (agencja_nieruchomosci)
Q1 <- quantile(dane$stories, 0.25, na.rm = TRUE)
Q3 <- quantile(dane$stories, 0.75, na.rm = TRUE)
IQR <- Q3 - Q1

lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
outliers <- dane$stories[dane$stories < lower_bound | dane$stories > upper_bound]

boxplot(dane$stories, main = "Wartości odstające w zmiennej 'stories'",
        col = "lightblue", horizontal = TRUE)

dane_cleaned <- dane %>%
  filter(stories >= lower_bound, stories <= upper_bound)

boxplot(dane_cleaned$stories, main = "wykres pudełkowy dla zmiennej 'stories' bez wartości odstających",
        col = "lightblue", horizontal = TRUE)

Wykres pudełkowy dla zmiennej “stories” bez wartości odstających - został nałożony filtr nieuwzględniający wartości odstających.

4 Wizualizacja i analiza opisowa danych

Po oczyszczeniu zbioru danych przeprowadzono eksploracyjną analizę danych (EDA) w celu zrozumienia ich rozkładu oraz zależności pomiędzy zmiennymi. Wykorzystano histogramy, wykresy punktowe oraz wykresy skrzynkowe do przedstawienia kluczowych zależności.

Wyniki analizy wskazują na silny związek między ceną a powierzchnią domu. Dodatkowo, liczba miejsc parkingowych oraz liczba pięter również wydają się mieć wpływ na wartość nieruchomości. W szczególności, nieruchomości posiadające 3 sypialnie wykazują większą liczbę brakujących wartości w kolumnie cena, co może wskazywać na specyficzne strategie rynkowe.

completed_data$price <- completed_data$price / 1000
completed_data$area <- completed_data$area * 0.092903
ggplot(completed_data, aes(x=price))+
  geom_histogram(bins = 10)+
  labs(title="Ilośc domów z przedziałami cenowymi", x="Price", y="Ilość domów")+
  theme_bw() +
  facet_grid(~parking)

4.1 Zależność ceny od wielkości domu w podziale na połaczenie do głównej drogi

completed_data$bedfac <- as.factor(completed_data$bedrooms)

ggplot(completed_data, aes(x = area, y = price, color = bedfac)) +
  geom_point(alpha = 0.7, size = 3) +
  scale_color_brewer(palette = "Set1") +
  theme_ipsum() +
  facet_grid(~mainroad) +
  labs(
    x = "Wielkość domu",
    y = "Cena",
    color = "Liczba sypialni")+
  theme( text = element_text(family = "sans"))

4.2 Zależność ceny od sypialni z podziałem na “prefarea”

ggplot(completed_data, aes(x = bedrooms, y = price, color = bedfac)) +
  geom_point(alpha = 0.7, size = 3) +
  scale_color_brewer(palette = "Set1") +
  theme_ipsum() +
  theme(
    text = element_text(family = "sans")  ## Użyj czcionki sans
  ) +
  facet_grid(~prefarea) +
  labs(
    x = "ilość sypialni",
    y = "Cena",
    color = "Liczba sypialni"
  )

4.3 wykres ceny w zaleznosci od metrazu

completed_data$prefarea1 <- as.factor(completed_data$prefarea)  ## Zamiana na factor

p <- ggplot(completed_data, aes(x = price, y = area)) +
  geom_point(aes(color = prefarea1)) +  
  geom_smooth(method = "lm", se = TRUE)+
  xlab("Cena")+
  ylab("Powierzchnia domu")+
  scale_color_discrete(name = "Preferowane miejsce")+
  ggtitle("Metraż oraz cena domu")+
  theme_light()

plotly::ggplotly(p)

4.4 Rozkład powierzchni domu w zależności od liczby pięter

Poniższy wykres przedstawia rozkład powierzchni domu w zależności od liczby pięter i ceny. Dzięki niej możemy zaobserwować, iż najwięcej mieszkań posiada 1 bądź 2 piętra. Dodatkowo możemy przeanalizować jak kształtuje się cena wśród takich mieszkań. Interpretując wykres dostrzegamy, iż im mniejsza powierzchnia oraz liczba pięter tym niższa cena.

completed_data$pietra <- as.factor(completed_data$stories)

ggplot(completed_data, aes(x = stories, y = area)) +
  geom_boxplot(aes(group = stories), outlier.shape = NA) + 
  geom_jitter(aes(color = price), width = 0.2, alpha = 0.7) +
  scale_color_gradient(name = "Cena", low = "lightblue", high = "navyblue") +
  xlab("Liczba pięter") +
  ylab("Powierzchnia domu") +
  ggtitle("Rozkład powierzchni domu w zależności od liczby pięter i ceny") +
  theme_minimal()

By móc się dokładnie przyjrzeć jak prezentują się wartości dt. powierzchni w podziale na liczbę pokoi została stworzona tabela. Najwyższy średni metraż posiadają mieszkania 4 pokojowe, co nie powinno dziwić. Natomiast najniższy średni metraż oraz medianę posiadają mieszkania 2 pokojowe.

summary_table <- completed_data %>%
  group_by(pietra) %>%
  summarise(
    Srednia_powierzchnia = mean(area, na.rm = TRUE),
    Mediana_powierzchnia = median(area, na.rm = TRUE),
    Std_powierzchnia = sd(area, na.rm = TRUE),
    Srednia_cena = mean(price, na.rm = TRUE),
    Mediana_cena = median(price, na.rm = TRUE),
    Std_cena = sd(price, na.rm = TRUE),
    Liczba_obserwacji = n()
  )
knitr::kable(summary_table)
pietra Srednia_powierzchnia Mediana_powierzchnia Std_powierzchnia Srednia_cena Mediana_cena Std_cena Liczba_obserwacji
1 491.4405 418.0635 211.4452 4155.658 3815 1367.579 227
2 441.9345 386.9410 197.1835 4723.147 4200 1841.284 238
3 493.9319 510.9665 183.7000 5451.385 5810 1289.063 39
4 604.4428 557.4180 111.2456 7152.962 7245 1518.262 41

4.5 Kategoryzacja cen poprzez średnią i odchylenie standardowe

mean_price <- mean(completed_data$price)
sd_price <- sd(completed_data$price)

completed_data$z_score <- (completed_data$price - mean_price) / sd_price

completed_data$price_category <- cut(
  completed_data$z_score,
  breaks = c(-Inf, -1, 1, Inf),
  labels = c("tanie", "średnie", "drogie")
)

category_counts <- completed_data %>%
  group_by(price_category) %>%
  summarise(count = n())

4.6 Rozkład cen na kategorie: tani, średni i drogi

Wykres przedstawia ceny nieruchomości, które należą do agencji nieruchomości. Poprzez stworzenie 3 znaczników - tani, średni, drogi możemy przeanalizować w jaki sposób plasują się ceny z bazy danych.

ggplot(category_counts) +
  aes(
    x0 = 0, y0 = 0,
    r0 = 0, r = 1,
    amount = count,
    fill = price_category
  ) +
  geom_arc_bar(stat = "pie") +
  coord_fixed()

etykiety<-c("1750-2750 kPLN","2750-3750 kPLN","3750-4750 kPLN","4750-5750 kPLN","5750-6750 kPLN","6750-7750 kPLN","7750-8750 kPLN","9750-10750 kPLN","10750-11750 kPLN","11750-12250 kPLN")
limits<-cut(completed_data$price,seq(1750,12250,by=1000),labels=etykiety)
tabela1<-freq(limits)
knitr::kable(tabela1)
n % val%
1750-2750 kPLN 35 6.4 6.5
2750-3750 kPLN 145 26.6 26.9
3750-4750 kPLN 146 26.8 27.1
4750-5750 kPLN 87 16.0 16.1
5750-6750 kPLN 66 12.1 12.2
6750-7750 kPLN 24 4.4 4.5
7750-8750 kPLN 21 3.9 3.9
9750-10750 kPLN 8 1.5 1.5
10750-11750 kPLN 5 0.9 0.9
11750-12250 kPLN 2 0.4 0.4
NA 6 1.1 NA

4.7 Statystyka opisowa

Poniższy wykres kolumnowy przedstawia rozkład cen mieszkań z podziałem co 1000. Można wywnioskować, iż tak jak na wykresie kołowym, większość mieszkań jest blisko średniej ceny rynkowej. Tańsze mieszkania oraz droższe są w zdecydowanej mniejszości. Dodatkowo zaznaczono liniami jak kształtują się ceny mieszkań w podziale na ilość sypialni.

hist(completed_data$price, breaks="FD", col="green", probability = TRUE,
     main="Ceny nieruchomości")

lines(density(completed_data$price[completed_data$bedrooms == 1]), col=2)
lines(density(completed_data$price[completed_data$bedrooms == 2]), col=3)
lines(density(completed_data$price[completed_data$bedrooms == 3]), col=4)

legend("topright", 
       legend=c("Jedna sypialnia", "Dwie sypialnie", "Trzy sypialnie"),
       col=c(2, 3, 4), 
       lty=1, 
       horiz=FALSE, 
       box.lty=0, 
       cex=0.8)

completed_data %>%
  select(price, bedfac) %>%
  tbl_summary(
    by=bedfac,
    type = all_continuous() ~ "continuous2",
    statistic = all_continuous() ~ c(
      "{N_nonmiss}","{mean}","{sd}",
      "{median} ({p25}, {p75})",
      "{min}, {max}"),
    missing = "no",
    label = price ~ "Cena") %>%
  modify_header(label ~ "**Zmienna**") %>%
  modify_caption("**Rozkład cen wg liczby pokoi**") %>%
  bold_labels() %>% 
  add_p(pvalue_fun = ~ style_pvalue(.x, digits = 2))
Rozkład cen wg liczby pokoi
Zmienna 1
N = 2
2
N = 136
3
N = 300
4
N = 95
5
N = 10
6
N = 2
p-value
Cena






    N Non-missing 2 136 300 95 10 2
    Mean 2,713 3,647 4,919 5,551 5,925 4,792
    SD 619 990 1,678 2,147 2,339 1,826
    Median (Q1, Q3) 2,713 (2,275, 3,150) 3,500 (2,951, 4,200) 4,550 (3,640, 5,950) 5,250 (4,060, 6,300) 5,583 (3,850, 8,120) 4,792 (3,500, 6,083)
    Min, Max 2,275, 3,150 1,750, 7,070 1,750, 12,250 2,100, 12,250 3,010, 10,150 3,500, 6,083

Mieszkania 5-pokojowe mają najwyższą średnią cenę spośród wszystkich typów mieszkań icechują się również najwyższym odchyleniem standardowym, co oznacza największe wahania cen w tej kategorii. Mieszkania 1-pokojowe mają najniższą medianę ceny, co sugeruje, że to w tej grupie najczęściej znajdziemy najtańsze nieruchomości. Najwyższe ceny maksymalne występują jednak w mieszkaniach 3- i 4-pokojowych.

5 Wnioskowanie statystyczne

W tej sekcji przeprowadzono analizę statystyczną, mającą na celu wyciągnięcie wniosków na podstawie dostępnych danych. Wykorzystano testy statystyczne oraz modele predykcyjne do oceny istotności zależności między zmiennymi. Przeanalizowano m.in. regresję liniową do przewidywania cen nieruchomości oraz testy istotności statystycznej w celu oceny wpływu poszczególnych cech na wartość nieruchomości.

Wyniki analizy wskazują, że zmienne takie jak powierzchnia mieszkania, liczba pokoi oraz lokalizacja mają istotny wpływ na cenę nieruchomości. Regresja liniowa wykazała wysoką korelację między powierzchnią a ceną, natomiast testy statystyczne potwierdziły istotność wpływu lokalizacji na końcową wartość nieruchomości.

5.1 Wykres wiolinowy

Podstawowe statystyki opisowe cen nieruchomości jeżeli znajdują się w preferowanym obszarze (tak/nie - 1/0). Dla obu przypadków są widoczne obserwacje odstające dla zmiennej price. Mediana cen nieruchomości dla tych znajdujących się w preferowanej okolicy jest większa. W bazie więcej jest nieruchomości, które nie znajdują sie w preferowanej okolicy. W przypadku nieruchomości nieznajdujących się w preferowanym obszarze, pierwsza część obserwacji ma większe zagęszczenie przy niskiej cenie 2 - 4 tys., a ceny nad medianą mają większą rozpiętość - od około 4 do 12 tys. Dla nieruchomości w preferowanym obszarze wiolina jest najszersza w okolicach mediany - najwięcej obserwacji kiedy cena jest bliska wartości środkowej - około 6 tys.

data(completed_data)

completed_data %>%
  filter(prefarea %in% c(0, 1))%>%
  ggbetweenstats(
    y=price,
    x=prefarea
  )

5.2 Wykres kołowy

Jaki procent nieruchomości o powierzchni z danego przedziału posiada posiada wskazaną liczbę łazienek. Nawet w domach o największej powierzni jest tylko jedna łazienka.

completed_data$area_category <- cut(
  completed_data$area, 
  breaks = c(0, 2000, 5000, 10000, 16200),
  labels = c("0-2000", "2001-5000", "5001-10000", "10001-1620"),
  right = TRUE
)

completed_data %>%
  filter(bathrooms %in% c(1, 2 ,3, 4))%>%
  ggpiestats(
    y=area_category,
    x=bathrooms
  )

Jaki procent nieruchomości o powierzchni z danego przedziału posiada posiada wskazaną liczbę łazienek. Nawet w domach o największej powierzni dominuje tylko jedna łazienka. 4 łazienki ma jedynie jedna nieruchomość w zbiorze danych, na wykresie jest nawet niewidoczna - występuje w nieruchomość z przedziału 5001 - 10 000. Co ciekawe, większy procent nieruchomości z dwiema łazienkami jest dla nieruchomości z przedziału 5001 - 10 000, niż z 10 001 - 16 200.

5.3 Korelogram

df_filtered <- completed_data %>% select(-c(price_z, price_minmax, z_score))
numeric_cols <- sapply(df_filtered, is.numeric)
cor_matrix <- cor(df_filtered[, numeric_cols], use = "complete.obs")
corrplot(cor_matrix, method = "color", type = "upper", tl.col = "black", tl.cex = 0.8)

Ten wykres przedstawia macierz korelacji zmiennych numerycznych w zbiorze danych dotyczącym nieruchomości. Cena (price) wykazuje silną pozytywną korelację z powierzchnią (area), liczbą sypialni (bedrooms) i liczbą łazienek (bathrooms). Zmienna hotwaterheating nie ma bardzo niską korelację z pozostałymi zmiennymi. Co ciekawe zmienne Stories i basement mają ujemną korelację, oznacza to że im więcej pięter w budynku, bardziej prawdopodobne że nie ma mieszkanie nie ma piwnicy. Podobna zależność jest między ogrzewaniem ciepłą wodą (hotwaterheating) oraz dostępnością klimatyzacji (airconditioning).

6 Podsumowanie i wnioski

Raport przedstawia kompleksową analizę danych dotyczących nieruchomości, koncentrując się na brakujących wartościach, czyszczeniu danych oraz metodach imputacji. Dane zostały poddane weryfikacji pod kątem błędów logicznych i spójności, a następnie przekształcone w celu ułatwienia dalszej analizy. Kluczowe wnioski:

  1. Brakujące dane – Największy problem stanowi brak informacji o cenach nieruchomości. Analiza wskazuje, że brak tych danych może wynikać z praktyk rynkowych, np. ukrywania cen w celu negocjacji.
  2. Weryfiacjka jakości danych – Przyjęte reguły walidacji wykazały, że wszystkie wartości mieszczą się w logicznych zakresach, co pozwoliło na dalsze przetwarzanie danych.
  3. Imputacja brakujących wartości – Zastosowano różne metody uzupełniania brakujących danych. Algorytm k najbliższych sąsiadów (kNN) do imputacji brakujących cen. Regresję liniową do uzupełnienia zmiennych binarnych. Metodę MICE do imputacji danych wielowymiarowych.
  4. Transformacje i analiza odstających wartości – Wykryto i usunięto wartości odstające w kolumnie dotyczącej liczby pięter. Zastosowano także normalizację danych, co pozwoliło na bardziej precyzyjne analizy.
  5. Wizualizacja i eksploracja danych – Przedstawiono szereg wykresów ukazujących zależności między zmiennymi, m.in. wpływ liczby sypialni na cenę, powiązanie powierzchni nieruchomości z jej wartością oraz rozkład cen w zależności od dostępności do głównej drogi.
  6. Kategoryzacja cen – Na podstawie średniej i odchylenia standardowego dokonano podziału nieruchomości na trzy kategorie cenowe: tanie, średnie i drogie, co pozwoliło lepiej zrozumieć strukturę cenową zbioru danych.

Podjęte kroki znacząco poprawiły jakość danych i umożliwiły bardziej rzetelną analizę rynku nieruchomości. Zastosowane metody przetwarzania i imputacji danych pozwalają na dalsze wykorzystanie zbioru do prognozowania cen oraz analizy trendów rynkowych. W niniejszyn raporcie skupiono się na technicznych aspektach analizy danych, kolejnym krokiem mogłaby być bardziej zaawansowana analiza statystyczna, np. modelowanie regresyjne czy klasyfikacja, co pozwoliłoby na jeszcze głębsze zrozumienie rynku nieruchomości. Wturnym i mniej poruszonym zagadnieniem była sama formalna analiza opisowa danych.